home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 June / MacFormat 25.iso / Shareware City / Developers / OutOfPhase1.1 Source / OutOfPhase Folder / Level 0 Macintosh 01Jan95 / EventLoop.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-21  |  29.1 KB  |  998 lines  |  [TEXT/KAHL]

  1. /* EventLoop.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Debug.h"
  21. #include "Audit.h"
  22. #include "Definitions.h"
  23.  
  24. #ifdef THINK_C
  25.     #pragma options(pack_enums)
  26. #endif
  27. #include <memory.h>
  28. #include <menus.h>
  29. #include <AppleEvents.h>
  30. #include <Events.h>
  31. #include <Windows.h>
  32. #include <Quickdraw.h>
  33. #include <Desk.h>
  34. #include <ToolUtils.h>
  35. #include <GestaltEqu.h>
  36. #include <Power.h>
  37. #ifdef THINK_C
  38.     #pragma options(!pack_enums)
  39. #endif
  40.  
  41. #include "MyMalloc.h"
  42. #include "EventLoop.h"
  43. #include "Memory.h"
  44. #include "Menus.h"
  45.  
  46.  
  47. /* how many 1/60ths of a second occur between mouse image update events */
  48. #define CURSORUPDATEDELAY (15)
  49.  
  50. /* how many 1/60ths of a second should we leave menu hilited */
  51. #define MENUDELAY (4)
  52.  
  53. /* how many 1/60ths of a second between WaitNextEvents() when calling */
  54. /* RelinquishCPUJudiciously while application is in the foreground. */
  55. #define JUDICIOUSDELAYFOREGROUND (19)
  56.  
  57. /* same as JUDICIOUSDELAYFOREGROUND, except for when application is in background */
  58. #define JUDICIOUSDELAYBACKGROUND (5)
  59.  
  60. /* timeout factor for event loop when mouse is down (to facilitate mouse tracking) */
  61. #define VERYSMALLTIMEINTERVAL (1)
  62.  
  63. /* what timeout should RelinquishCPU (not judiciously) use */
  64. #define RELINQUISHCPUNORMALDELAY (5)
  65.  
  66. /* how long should we beep or invert the menu bar */
  67. #define BEEPDURATION (20)
  68.  
  69. /* possible states that the menu handling stuff could be in */
  70. typedef enum
  71.     {
  72.         eNoMenu EXECUTE(= -8764),
  73.         eMenuPendingMouse,
  74.         eMenuPendingKey,
  75.         eMenuSelected
  76.     } MenuStates;
  77.  
  78.  
  79. /* NIL = no mouse down; otherwise, it's the window that the mouse went down in */
  80. /* so that the mouse up event can be reported to the same window. */
  81. static WindowPtr            LastMouseDownInThisWindow = NIL;
  82.  
  83. /* when did we last check the mouse cursor image. */
  84. static long                        LastCursorCheck = 0;
  85.  
  86. /* when was the last time we actually waited for an event (this is used by */
  87. /* RelinquishCPUJudiciously to keep from doing it too often) */
  88. static long                        LastEventTime = 0;
  89.  
  90. /* what is the current timeout for WaitNextEvent */
  91. static long                        SleepTime = CURSORUPDATEDELAY;
  92.  
  93. /* what window was the last one that was active (for detecting changes) */
  94. static WindowPtr            LastActiveWindow = NIL;
  95.  
  96. /* flag indicating whether we are in the foreground or not */
  97. static MyBoolean            RunningInForeground = True;
  98.  
  99. /* sticky flag that remembers if the user tried to cancel during RelinquishCPU */
  100. static MyBoolean            CancelPending = False;
  101.  
  102. /* buffer for keys received during RelinquishCPU.  if it is NIL, then the buffer */
  103. /* does not exist. */
  104. static EventRecord*        KeyboardEventBuffer = NIL;
  105.  
  106. /* this is used during RelinquishCPUJudiciously to keep PowerBooks from */
  107. /* going into idle state */
  108. static MyBoolean            IsThisAPowerBook = False;
  109.  
  110. /* current delay for RelinquishCPUJudiciously.  It depends on RunningInForeground */
  111. static long                        JudiciousInterval = JUDICIOUSDELAYFOREGROUND;
  112.  
  113. /* flag that tells whether we should make beeping noises or not */
  114. static MyBoolean            MakeErrorBeeps = True;
  115.  
  116.  
  117. #if (CURRENTPROCTYPE == PROC68000) || (CURRENTPROCTYPE == PROC68020)
  118.     /* efficiency hack for 680x0 Macs */
  119.     #define TickCount() (*((volatile unsigned long*)0x016a))
  120. #else
  121.     /* 'nicer' for PowerPC */
  122. #endif
  123.  
  124.  
  125. /* initialize internal event loop data structures */
  126. MyBoolean                    Eep_InitEventLoop(void)
  127.     {
  128.         OSErr                        Error;
  129.         long                        Result;
  130.  
  131.         /* allocate keyboard buffer.  if this fails & it returns NIL, it's ok. */
  132.         KeyboardEventBuffer = (EventRecord*)AllocPtrCanFail(0,"KeyboardEventBuffer");
  133.         /* figure out if we are running on a sleepy powerbook */
  134.         Error = Gestalt(gestaltPowerMgrAttr,&Result);
  135.         IsThisAPowerBook = ((Result & (1 << gestaltPMgrExists)) != 0) && (Error == noErr);
  136.         return True;
  137.     }
  138.  
  139.  
  140. /* dispose of any internal event loop structures */
  141. void                            Eep_ShutdownEventLoop(void)
  142.     {
  143.         if (KeyboardEventBuffer != NIL)
  144.             {
  145.                 ReleasePtr((char*)KeyboardEventBuffer);
  146.             }
  147.         KeyboardEventBuffer = NIL;
  148.     }
  149.  
  150.  
  151. /* returns the ID number of the current window.  Returns 0 if there are no windows */
  152. static WinType*    GetCurrentWindow(void)
  153.     {
  154.         if ((FrontWindow() != NIL) && RunningInForeground)
  155.             {
  156.                 CheckPtrExistence((WinType*)GetWRefCon(FrontWindow()));
  157.                 return (WinType*)GetWRefCon(FrontWindow());
  158.             }
  159.          else
  160.             {
  161.                 return NIL;
  162.             }
  163.     }
  164.  
  165.  
  166. /* local routine that converts Toolbox modifier flags to our own modifier flags */
  167. static short            FormModifiers(short Modifiers)
  168.     {
  169.         short                        Value;
  170.  
  171.         Value = 0;
  172.         if ((Modifiers & shiftKey) != 0)
  173.             {
  174.                 Value |= eShiftKey;
  175.             }
  176.         if ((Modifiers & controlKey) != 0)
  177.             {
  178.                 Value |= eControlKey;
  179.             }
  180.         if ((Modifiers & cmdKey) != 0)
  181.             {
  182.                 Value |= eCommandKey;
  183.             }
  184.         if ((Modifiers & optionKey) != 0)
  185.             {
  186.                 Value |= eOptionKey;
  187.             }
  188.         if ((Modifiers & alphaLock) != 0)
  189.             {
  190.                 Value |= eCapsLockKey;
  191.             }
  192.         if ((Modifiers & btnState) != 0)
  193.             {
  194.                 Value |= eMouseDownFlag;
  195.             }
  196.         return Value;
  197.     }
  198.  
  199.  
  200. /* Fetch an event from the event queue and return it.  Only some of the parameters */
  201. /* returned may be valid; see the enumeration comments above for EventType to see */
  202. /* which.  Mouse coordinates are always local to the current window, or undefined */
  203. /* if there is no current window.  If there are no events, the routine will return */
  204. /* after some amount of time.  This routine may call the Menu manager, so menus */
  205. /* should be initialized before this routine is called.  Any parameter may be passed */
  206. /* as NIL if the user doesn't care about the result.  Window changes do not occur */
  207. /* if the mouse is down.  If the current window is a dialog box, then a window */
  208. /* change will never be returned for another window.  Mouse up events are always */
  209. /* returned with the same window as the mosue down event, even if the mouse is no */
  210. /* longer in the window. */
  211. EventType                    GetAnEvent(OrdType* Xloc, OrdType* Yloc, ModifierFlags* Modifiers,
  212.                                         WinType** Window, MenuItemType** MenuCommand, char* KeyPressed)
  213.     {
  214.         /* the event.  statically allocated so that it is still valid when called */
  215.         /* again.  this is used for remembering the event that triggered a menu thing. */
  216.         static EventRecord        MyEvent;
  217.         /* state variable for menu handling */
  218.         static MenuStates            MenuState = eNoMenu;
  219.         /* when was the last menu selected (for slowing down menu bar flash) */
  220.         static unsigned long    WhenMenuWasSelected;
  221.  
  222.         WindowPtr                            WhichWindow;
  223.         long                                    MenuCommandInteger;
  224.  
  225.  
  226.         /* redraw the menu bar if it has changed */
  227.         Eep_RedrawMenuBar();
  228.  
  229.         /* the cancel pending flag for RelinquishCPU is sticky, so that if the user */
  230.         /* cancels once, it continues to return True.  It gets cleared the next time */
  231.         /* the program tries to handle a "real" event. */
  232.         CancelPending = False;
  233.  
  234.         /* redraw any windows that couldn't be redrawn when the event was received. */
  235.         PerformDeferredUpdates();
  236.  
  237.         /* main loop */
  238.      LoopPoint:
  239.  
  240.         /* check to see if active window has changed */
  241.         if (LastActiveWindow != FrontWindow())
  242.             {
  243.                 LastActiveWindow = FrontWindow();
  244.                 if (Window != NIL)
  245.                     {
  246.                         *Window = GetCurrentWindow();
  247.                     }
  248.                 return eActiveWindowChanged;
  249.             }
  250.  
  251.         /* if there are any queued up keypresses from RelinquishCPU, report them now */
  252.         if ((KeyboardEventBuffer != NIL) && (PtrSize((char*)KeyboardEventBuffer) > 0))
  253.             {
  254.                 EventRecord*    Temp;
  255.                 long                    OldSize;
  256.  
  257.                 CheckPtrExistence(KeyboardEventBuffer);
  258.                 /* a keypress was buffered during a RelinquishCPU call */
  259.                 MyEvent = KeyboardEventBuffer[0]; /* save the event */
  260.                 OldSize = PtrSize((char*)KeyboardEventBuffer);
  261.                 PRNGCHK(KeyboardEventBuffer,&(KeyboardEventBuffer[1]),
  262.                     OldSize - sizeof(EventRecord));
  263.                 PRNGCHK(KeyboardEventBuffer,&(KeyboardEventBuffer[0]),
  264.                     OldSize - sizeof(EventRecord));
  265.                 MoveData((char*)&(KeyboardEventBuffer[1]),(char*)&(KeyboardEventBuffer[0]),
  266.                     OldSize - sizeof(EventRecord));
  267.                 Temp = (EventRecord*)ResizePtr((char*)KeyboardEventBuffer,
  268.                     OldSize - sizeof(EventRecord));
  269.                 if (Temp == NIL)
  270.                     {
  271.                         /* if we run out of memory, then we just lose all the buffered keypresses */
  272.                         ReleasePtr((char*)KeyboardEventBuffer);
  273.                         KeyboardEventBuffer = (EventRecord*)AllocPtrCanFail(0,"KeyboardEventBuffer");
  274.                     }
  275.                  else
  276.                     {
  277.                         KeyboardEventBuffer = Temp;
  278.                     }
  279.                 /* now, jump so that we fake the event */
  280.                 goto HandleEventSwitchPoint;
  281.             }
  282.  
  283.         /* handle any menu operations that are in progress. */
  284.         switch (MenuState)
  285.             {
  286.                 default:
  287.                     EXECUTE(PRERR(ForceAbort,"GetAnEvent:  bad MenuState"));
  288.                     break;
  289.                 case eNoMenu:
  290.                     break; /* continue on through */
  291.                 case eMenuPendingMouse:
  292.                     /* let system process event */
  293.                     /* MyEvent is still valid from last time through */
  294.                     MenuCommandInteger = MenuSelect(MyEvent.where);
  295.                     /* if the menu was actually chosen, then return it */
  296.                     if (MenuCommand != NIL)
  297.                         {
  298.                             *MenuCommand = Eep_MMID2ItemID(MenuCommandInteger);
  299.                             if (*MenuCommand != NIL)
  300.                                 {
  301.                                     /* we only report a menu choice if the user actually chose something */
  302.                                     MenuState = eMenuSelected;
  303.                                     WhenMenuWasSelected = TickCount();
  304.                                     if (Modifiers != NIL)
  305.                                         {
  306.                                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  307.                                         }
  308.                                     if (Window != NIL)
  309.                                         {
  310.                                             *Window = GetCurrentWindow();
  311.                                         }
  312.                                     MenuState = eMenuSelected;
  313.                                     return eMenuCommand;
  314.                                 }
  315.                         }
  316.                     /* if menu wasn't chosen, then reset menu state & get another event */
  317.                     MenuState = eNoMenu;
  318.                     goto LoopPoint;
  319.                 case eMenuPendingKey:
  320.                     /* convert the key to a menu thing */
  321.                     /* MyEvent is still valid from last time through */
  322.                     MenuCommandInteger = MenuKey(MyEvent.message & charCodeMask);
  323.                     /* convert the menu command into something we can handle */
  324.                     if (MenuCommand != NIL)
  325.                         {
  326.                             /* only do menus if they can handle them */
  327.                             *MenuCommand = Eep_MMID2ItemID(MenuCommandInteger);
  328.                             /* if the menu actually happened, then report it */
  329.                             if (*MenuCommand != NIL)
  330.                                 {
  331.                                     WhenMenuWasSelected = TickCount();
  332.                                     if (Modifiers != NIL)
  333.                                         {
  334.                                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  335.                                         }
  336.                                     if (Window != NIL)
  337.                                         {
  338.                                             *Window = GetCurrentWindow();
  339.                                         }
  340.                                     MenuState = eMenuSelected;
  341.                                     return eMenuCommand;
  342.                                 }
  343.                         }
  344.                     /* if the key doesn't correspond to a menu item, then schlep on over */
  345.                     /* to the normal keypress handler */
  346.                     MenuState = eNoMenu;
  347.                     goto FinishKeypressEvent;
  348.                     break;
  349.                 case eMenuSelected:
  350.                     /* make a small delay so that the menu is actually visible */
  351.                     while (TickCount() - WhenMenuWasSelected < MENUDELAY)
  352.                         {
  353.                             /* hideous delay loop to flash menu so user can see it. */
  354.                         }
  355.                     HiliteMenu(0);
  356.                     MenuState = eNoMenu;
  357.                     break;
  358.             }
  359.  
  360.         /* call the event routine.  the ugly parameter decides whether a mouse is */
  361.         /* down and uses that to speed up mouse tracking */
  362.         WaitNextEvent(everyEvent,&MyEvent,((LastMouseDownInThisWindow != NIL)
  363.             && (SleepTime > VERYSMALLTIMEINTERVAL)) ? VERYSMALLTIMEINTERVAL : SleepTime,NIL);
  364.  
  365.         /* remember last event time for RelinquishCPU */
  366.         LastEventTime = TickCount();
  367.  
  368.         /* decode the event */
  369.      HandleEventSwitchPoint:
  370.         switch (MyEvent.what)
  371.             {
  372.                 case nullEvent:
  373.                     if (TickCount() - LastCursorCheck >= CURSORUPDATEDELAY)
  374.                         {
  375.                             if (FrontWindow() != NIL)
  376.                                 {
  377.                                     LastCursorCheck = TickCount();
  378.                                     SetPort(FrontWindow());
  379.                                     GlobalToLocal(&MyEvent.where);
  380.                                     if (Xloc != NIL)
  381.                                         {
  382.                                             *Xloc = MyEvent.where.h;
  383.                                         }
  384.                                     if (Yloc != NIL)
  385.                                         {
  386.                                             *Yloc = MyEvent.where.v;
  387.                                         }
  388.                                     if (Modifiers != NIL)
  389.                                         {
  390.                                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  391.                                         }
  392.                                     if (Window != NIL)
  393.                                         {
  394.                                             *Window = GetCurrentWindow();
  395.                                         }
  396.                                     return eCheckCursor;
  397.                                 }
  398.                              else
  399.                                 {
  400.                                     SetCursor(&qd.arrow);
  401.                                 }
  402.                         }
  403.                     if (FrontWindow() != NIL)
  404.                         {
  405.                             SetPort(FrontWindow());
  406.                             GlobalToLocal(&MyEvent.where);
  407.                             if (Xloc != NIL)
  408.                                 {
  409.                                     *Xloc = MyEvent.where.h;
  410.                                 }
  411.                             if (Yloc != NIL)
  412.                                 {
  413.                                     *Yloc = MyEvent.where.v;
  414.                                 }
  415.                         }
  416.                     if (Modifiers != NIL)
  417.                         {
  418.                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  419.                         }
  420.                     if (Window != NIL)
  421.                         {
  422.                             *Window = GetCurrentWindow();
  423.                         }
  424.                     return eNoEvent;
  425.  
  426.                 case mouseDown:
  427.                     switch (FindWindow(MyEvent.where,&WhichWindow))
  428.                         {
  429.                             case inSysWindow:
  430.                                 EXECUTE(PRERR(AllowResume,"GetAnEvent:  FindWindow returned inSysWindow"));
  431.                                 /* SystemClick(&MyEvent,WhichWindow); */
  432.                                 goto LoopPoint;
  433.                             case inMenuBar:
  434.                                 MenuState = eMenuPendingMouse;
  435.                                 WipeMenusClean();
  436.                                 if (Modifiers != NIL)
  437.                                     {
  438.                                         *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  439.                                     }
  440.                                 if (Window != NIL)
  441.                                     {
  442.                                         *Window = GetCurrentWindow();
  443.                                     }
  444.                                 return eMenuStarting;
  445.                             case inDrag:
  446.                                 if ((FrontWindow() != NIL) /* make sure there's a front window */
  447.                                     && (
  448.                                         /* front window must be a document window */
  449.                                         (GetWindowKind((WinType*)GetWRefCon(FrontWindow())) == eDocumentWindow)
  450.                                         /* or front window must be the window in question */
  451.                                         || (FrontWindow() == WhichWindow)
  452.                                         /* or command key must be down to prevent window switch */
  453.                                         || ((MyEvent.modifiers & cmdKey) != 0)))
  454.                                     {
  455.                                         Rect                    BoundsRect;
  456.                                         RgnHandle            BoundsRegion;
  457.  
  458.                                         BoundsRegion = GetGrayRgn();
  459.                                         BoundsRect = (**BoundsRegion).rgnBBox;
  460.                                         InsetRect(&BoundsRect,4,4);
  461.                                         DragWindow(WhichWindow,MyEvent.where,&BoundsRect);
  462.                                     }
  463.                                  else
  464.                                     {
  465.                                         ErrorBeep();
  466.                                     }
  467.                                 goto LoopPoint;
  468.                             case inContent:
  469.                             case inGrow:
  470.                                 if (FrontWindow() != WhichWindow)
  471.                                     {
  472.                                         if ((FrontWindow() == NIL) || (GetWindowKind(
  473.                                             (WinType*)GetWRefCon(FrontWindow())) == eDocumentWindow))
  474.                                             {
  475.                                                 SelectWindow(WhichWindow);
  476.                                             }
  477.                                          else
  478.                                             {
  479.                                                 ErrorBeep();
  480.                                             }
  481.                                         goto LoopPoint;
  482.                                     }
  483.                                 LastMouseDownInThisWindow = WhichWindow;
  484.                                 SetPort(WhichWindow);
  485.                                 GlobalToLocal(&MyEvent.where);
  486.                                 if (Modifiers != NIL)
  487.                                     {
  488.                                         *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  489.                                     }
  490.                                 if (Xloc != NIL)
  491.                                     {
  492.                                         *Xloc = MyEvent.where.h;
  493.                                     }
  494.                                 if (Yloc != NIL)
  495.                                     {
  496.                                         *Yloc = MyEvent.where.v;
  497.                                     }
  498.                                 if (Window != NIL)
  499.                                     {
  500.                                         *Window = (WinType*)GetWRefCon(WhichWindow);
  501.                                     }
  502.                                 return eMouseDown;
  503.                             case inGoAway:
  504.                                 if (TrackGoAway(WhichWindow,MyEvent.where))
  505.                                     {
  506.                                         if (Window != NIL)
  507.                                             {
  508.                                                 *Window = (WinType*)GetWRefCon(WhichWindow);
  509.                                             }
  510.                                         return eWindowClosing;
  511.                                     }
  512.                                 goto LoopPoint;
  513.                             case inZoomIn:
  514.                                 SetPort(WhichWindow);
  515.                                 if (TrackBox(WhichWindow,MyEvent.where,inZoomIn))
  516.                                     {
  517.                                         ZoomWindow(WhichWindow,inZoomIn,False);
  518.                                         if (Window != NIL)
  519.                                             {
  520.                                                 *Window = (WinType*)GetWRefCon(WhichWindow);
  521.                                             }
  522.                                         return eWindowResized;
  523.                                     }
  524.                                 goto LoopPoint;
  525.                             case inZoomOut:
  526.                                 SetPort(WhichWindow);
  527.                                 if (TrackBox(WhichWindow,MyEvent.where,inZoomOut))
  528.                                     {
  529.                                         ZoomWindow(WhichWindow,inZoomOut,False);
  530.                                         if (Window != NIL)
  531.                                             {
  532.                                                 *Window = (WinType*)GetWRefCon(WhichWindow);
  533.                                             }
  534.                                         return eWindowResized;
  535.                                     }
  536.                                 goto LoopPoint;
  537.                             default:
  538.                                 goto LoopPoint;
  539.                         }
  540.                     break;
  541.  
  542.                 case mouseUp:
  543.                     if (LastMouseDownInThisWindow == NIL)
  544.                         {
  545.                             /* orphaned mouse-up, probably munched during RelinquishCPU */
  546.                             goto LoopPoint;
  547.                         }
  548.                     CheckPtrExistence((WinType*)GetWRefCon(LastMouseDownInThisWindow));
  549.                     SetPort(LastMouseDownInThisWindow);
  550.                     GlobalToLocal(&MyEvent.where);
  551.                     if (Modifiers != NIL)
  552.                         {
  553.                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  554.                         }
  555.                     if (Xloc != NIL)
  556.                         {
  557.                             *Xloc = MyEvent.where.h;
  558.                         }
  559.                     if (Yloc != NIL)
  560.                         {
  561.                             *Yloc = MyEvent.where.v;
  562.                         }
  563.                     if (Window != NIL)
  564.                         {
  565.                             *Window = (WinType*)GetWRefCon(LastMouseDownInThisWindow);
  566.                         }
  567.                     LastMouseDownInThisWindow = NIL;
  568.                     return eMouseUp;
  569.  
  570.                 case keyDown:
  571.                 case autoKey:
  572. #if DEBUG
  573.                     if ((MyEvent.modifiers & cmdKey) && (MyEvent.modifiers & shiftKey)
  574.                         && ((MyEvent.message & charCodeMask) == 'h'))
  575.                         {
  576.                             CheckFragmentation();
  577.                             goto LoopPoint;
  578.                         }
  579. #endif
  580.                     if ((MyEvent.modifiers & cmdKey) != 0)
  581.                         {
  582.                             if ((MyEvent.message & charCodeMask) == '.')
  583.                                 {
  584.                                     if (KeyPressed != NIL)
  585.                                         {
  586.                                             *KeyPressed = eCancelKey;
  587.                                         }
  588.                                     goto KeypressCancelSkipCode;
  589.                                 }
  590.                             MenuState = eMenuPendingKey;
  591.                             WipeMenusClean();
  592.                             if (Modifiers != NIL)
  593.                                 {
  594.                                     *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  595.                                 }
  596.                             if (Window != NIL)
  597.                                 {
  598.                                     *Window = GetCurrentWindow();
  599.                                 }
  600.                             return eMenuStarting;
  601.                         }
  602.                     /* the menu keypress handler jumps here to handle commanded keys */
  603.                  FinishKeypressEvent:
  604.                     if (KeyPressed != NIL)
  605.                         {
  606.                             *KeyPressed = MyEvent.message & charCodeMask;
  607.                         }
  608.                     /* jump here if *KeyPressed is eCancelKey and you don't want */
  609.                     /* to clobber it by storing something else in it */
  610.                  KeypressCancelSkipCode:
  611.                     if (FrontWindow() != NIL)
  612.                         {
  613.                             SetPort(FrontWindow());
  614.                             GlobalToLocal(&MyEvent.where);
  615.                             if (Xloc != NIL)
  616.                                 {
  617.                                     *Xloc = MyEvent.where.h;
  618.                                 }
  619.                             if (Yloc != NIL)
  620.                                 {
  621.                                     *Yloc = MyEvent.where.v;
  622.                                 }
  623.                         }
  624.                     if (Modifiers != NIL)
  625.                         {
  626.                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  627.                         }
  628.                     if (Window != NIL)
  629.                         {
  630.                             *Window = GetCurrentWindow();
  631.                         }
  632.                     return eKeyPressed;
  633.  
  634.                 case keyUp:
  635.                     goto LoopPoint;
  636.  
  637.                 case updateEvt:
  638.                     BeginUpdate((WindowPtr)MyEvent.message);
  639.                     CallWindowUpdate((WinType*)GetWRefCon((WindowPtr)MyEvent.message));
  640.                     EndUpdate((WindowPtr)MyEvent.message);
  641.                     goto LoopPoint;
  642.  
  643.                 case activateEvt:
  644.                     /* we'll catch this when we test FrontWindow() */
  645.                     goto LoopPoint;
  646.  
  647.                 case osEvt:
  648.                     switch ((unsigned char)((MyEvent.message >> 24) & 0x000000ff))
  649.                         {
  650.                             case suspendResumeMessage:
  651.                                 if (!(MyEvent.message & resumeFlag))
  652.                                     {
  653.                                         /* suspend */
  654.                                         JudiciousInterval = JUDICIOUSDELAYBACKGROUND;
  655.                                         RunningInForeground = False;
  656.                                         if (Window != NIL)
  657.                                             {
  658.                                                 *Window = 0;
  659.                                             }
  660.                                     }
  661.                                  else
  662.                                     {
  663.                                         /* resume */
  664.                                         JudiciousInterval = JUDICIOUSDELAYFOREGROUND;
  665.                                         RunningInForeground = True;
  666.                                         if (Window != NIL)
  667.                                             {
  668.                                                 *Window = GetCurrentWindow();
  669.                                             }
  670.                                     }
  671.                                 return eActiveWindowChanged;
  672.                             case mouseMovedMessage:
  673.                                 break;
  674.                             default:
  675.                                 break;
  676.                         }
  677.                     goto LoopPoint;
  678.  
  679.                 case kHighLevelEvent:
  680.                     AEProcessAppleEvent(&MyEvent);
  681.                     goto LoopPoint;
  682.  
  683.                 default:
  684.                     goto LoopPoint;
  685.             }
  686.     }
  687.  
  688.  
  689. /* set the amount of time to wait for an event.  The default is 1/4 of a second */
  690. /* time units are in seconds (can be fractional since they are floating point values) */
  691. double                SetEventSleepTime(double TheSleepTime)
  692.     {
  693.         unsigned long            OldSleepTime;
  694.  
  695.         OldSleepTime = SleepTime;
  696.         SleepTime = (TheSleepTime * 60) + 0.5;
  697.         return (double)OldSleepTime / 60 + 0.5;
  698.     }
  699.  
  700.  
  701. /* used by RelinquishCPUCheckCancel.  RelinquishCPUCheckCancel continually */
  702. /* resets it's value to VERYSMALLTIMEINVERVAL, but if it is changed, then */
  703. /* the new delay will take effect for one call of RelinquishCPUCheckCancel. */
  704. /* RelinquishCPUJudiciouslyCheckCancel uses this to get the processor back */
  705. /* as soon as possible. */
  706. static long                    RelinqCPUDelay = RELINQUISHCPUNORMALDELAY;
  707.  
  708. /* relinquishes CPU for 1 tick and checks to see if the user hit escape or cmd-. */
  709. /* (or perhaps other cancel signals) and returns True if the user is trying to */
  710. /* cancel.  Through the use of the global variable CancelPending, cancels are */
  711. /* "sticky" so that subsequent calls will return True as well, until GetAnEvent is */
  712. /* called. */
  713. MyBoolean            RelinquishCPUCheckCancel(void)
  714.     {
  715.         EventRecord            StupidEvent;
  716.         WindowPtr                WhichWindow;
  717.  
  718.         if (LastMouseDownInThisWindow != NIL)
  719.             {
  720.                 /* since we munch mouse events, we don't want to do this during a mouse */
  721.                 /* down since we'll probably lose the mouse up.  It's rather silly for the */
  722.                 /* user to hit cancel while holding the mouse down anyway... */
  723.                 return CancelPending;
  724.             }
  725.      TryAgainPoint:
  726.         WaitNextEvent(keyDownMask | keyUpMask | mDownMask | mUpMask | updateMask
  727.             | osMask | autoKeyMask | diskMask,&StupidEvent,RelinqCPUDelay,NIL);
  728.         LastEventTime = TickCount();
  729.         switch (StupidEvent.what)
  730.             {
  731.                 case updateEvt:
  732.                     BeginUpdate((WindowPtr)StupidEvent.message);
  733.                     MarkForDeferredUpdate((WinType*)GetWRefCon((WindowPtr)StupidEvent.message));
  734.                     EndUpdate((WindowPtr)StupidEvent.message);
  735.                     goto TryAgainPoint;
  736.                 case mouseDown:
  737.                     switch (FindWindow(StupidEvent.where,&WhichWindow))
  738.                         {
  739.                             default:
  740.                                 break;
  741.                             case inSysWindow:
  742.                                 EXECUTE(PRERR(AllowResume,"GetAnEvent:  FindWindow returned inSysWindow"));
  743.                                 /* SystemClick(&StupidEvent,WhichWindow); */
  744.                                 break;
  745.                             case inDrag:
  746.                                 if ((FrontWindow() == WhichWindow) || ((FrontWindow() != NIL)
  747.                                     && ((GetWindowKind((WinType*)GetWRefCon(FrontWindow()))
  748.                                     == eDocumentWindow) || (GetWindowKind((WinType*)GetWRefCon(
  749.                                     FrontWindow())) == eModelessDialogWindow))
  750.                                     && ((StupidEvent.modifiers & cmdKey) != 0)))
  751.                                     {
  752.                                         Rect                    BoundsRect;
  753.                                         RgnHandle            BoundsRegion;
  754.  
  755.                                         BoundsRegion = GetGrayRgn();
  756.                                         BoundsRect = (**BoundsRegion).rgnBBox;
  757.                                         InsetRect(&BoundsRect,4,4);
  758.                                         DragWindow(WhichWindow,StupidEvent.where,&BoundsRect);
  759.                                     }
  760.                                  else
  761.                                     {
  762.                                         ErrorBeep();
  763.                                     }
  764.                                 break;
  765.                         }
  766.                     break;
  767.                 case keyDown:
  768.                     if ((((StupidEvent.message & charCodeMask) == '.')
  769.                         && ((StupidEvent.modifiers & cmdKey) != 0)) ||
  770.                         ((StupidEvent.message & charCodeMask) == 27))
  771.                         {
  772.                             CancelPending = True; /* stick */
  773.                         }
  774.                     else if (KeyboardEventBuffer != NIL)
  775.                         {
  776.                             EventRecord*        Temp;
  777.                             long                        OldSize;
  778.  
  779.                             CheckPtrExistence(KeyboardEventBuffer);
  780.                             /* maybe the keypress was wanted, so we should save it */
  781.                             OldSize = PtrSize((char*)KeyboardEventBuffer);
  782.                             Temp = (EventRecord*)ResizePtr((char*)KeyboardEventBuffer,
  783.                                 OldSize + sizeof(EventRecord));
  784.                             if (Temp != NIL)
  785.                                 {
  786.                                     Temp[OldSize / sizeof(EventRecord)] = StupidEvent;
  787.                                     KeyboardEventBuffer = Temp;
  788.                                 }
  789.                         }
  790.                     break;
  791.                 default:
  792.                     break;
  793.             }
  794.         RelinqCPUDelay = RELINQUISHCPUNORMALDELAY;
  795.         return CancelPending; /* sticky */
  796.     }
  797.  
  798.  
  799. /* similar to RelinquishCPUCheckCancel but in cooperative multitasking systems, */
  800. /* it gives much better performance for the application by not releasing the */
  801. /* processor nearly as often */
  802. MyBoolean            RelinquishCPUJudiciouslyCheckCancel(void)
  803.     {
  804.         if (TickCount() - LastEventTime < JudiciousInterval)
  805.             {
  806.                 return CancelPending; /* sticky */
  807.             }
  808.         RelinqCPUDelay = 0; /* come back right away */
  809.         if (IsThisAPowerBook)
  810.             {
  811.                 /* if some CPU intensive task is going on, then keep the processor */
  812.                 /* from getting sleepy */
  813.                 IdleUpdate();
  814.             }
  815.         return RelinquishCPUCheckCancel();
  816.     }
  817.  
  818.  
  819. /* read the system timer (in seconds).  The timer returns real time (not process */
  820. /* time) but not relative to any known time.  The value may roll over from an */
  821. /* undefined large number to 0. */
  822. double                ReadTimer(void)
  823.     {
  824.         return ((double)TickCount()) / 60;
  825.     }
  826.  
  827.  
  828. /* find the true difference between two timer values even if one has rolled over */
  829. double                TimerDifference(double Now, double Then)
  830.     {
  831.         return (double)((unsigned long)(60 * Now + 0.5)
  832.             - (unsigned long)(60 * Then + 0.5)) / 60;
  833.     }
  834.  
  835.  
  836. /* get the current mouse position.  If there is no current window, the */
  837. /* results are undefined.  Either of the parameters can be NIL if the user */
  838. /* doesn't care about the result */
  839. void                    GetMousePosition(OrdType* Xloc, OrdType* Yloc)
  840.     {
  841.         Point            MouseLoc;
  842.  
  843.         if (FrontWindow() != NIL)
  844.             {
  845.                 SetPort(FrontWindow());
  846.                 GetMouse(&MouseLoc);
  847.                 if (Xloc != NIL)
  848.                     {
  849.                         *Xloc = MouseLoc.h;
  850.                     }
  851.                 if (Yloc != NIL)
  852.                     {
  853.                         *Yloc = MouseLoc.v;
  854.                     }
  855.             }
  856.     }
  857.  
  858.  
  859. /* read the state of the modifier keys on the keyboard.  On systems that don't */
  860. /* allow this, the function may return the modifiers as they were at the last */
  861. /* known time */
  862. ModifierFlags    CheckModifiers(void)
  863.     {
  864.         EventRecord            StupidEvent;
  865.  
  866.         WaitNextEvent(0,&StupidEvent,0,NIL);
  867.         return (ModifierFlags)FormModifiers(StupidEvent.modifiers);
  868.     }
  869.  
  870.  
  871. /* set an implementation defined version of the specified cursor */
  872. void                    SetArrowCursor(void)
  873.     {
  874.         SetCursor(&qd.arrow);
  875.     }
  876.  
  877.  
  878. /* set an implementation defined version of the specified cursor */
  879. void                    SetIBeamCursor(void)
  880.     {
  881.         CursHandle        DaCursor;
  882.  
  883.         DaCursor = GetCursor(iBeamCursor);
  884.         HLock((Handle)DaCursor);
  885.         SetCursor(*DaCursor);
  886.         HUnlock((Handle)DaCursor);
  887.     }
  888.  
  889.  
  890. /* set an implementation defined version of the specified cursor */
  891. void                    SetWatchCursor(void)
  892.     {
  893.         CursHandle        DaCursor;
  894.  
  895.         DaCursor = GetCursor(watchCursor);
  896.         HLock((Handle)DaCursor);
  897.         SetCursor(*DaCursor);
  898.         HUnlock((Handle)DaCursor);
  899.     }
  900.  
  901.  
  902. /* set an implementation defined version of the specified cursor */
  903. void                    SetCrossHairCursor(void)
  904.     {
  905.         CursHandle        DaCursor;
  906.  
  907.         DaCursor = GetCursor(crossCursor);
  908.         HLock((Handle)DaCursor);
  909.         SetCursor(*DaCursor);
  910.         HUnlock((Handle)DaCursor);
  911.     }
  912.  
  913.  
  914. /* set the cursor tho the image and mask specified.  If the implementation's cursor */
  915. /* is larger than 16x16, then the adjustment is implementation defined.  On the */
  916. /* Macintosh, cursors are 16x16, so no adjustment is necessary */
  917. /* the most significant bit of the word is leftmost */
  918. void                            SetTheCursor(short HotPointX, short HotPointY,
  919.                                         unsigned short CursorImage[16], unsigned short CursorMask[16])
  920.     {
  921.         Cursor                    TheCurs;
  922.         int                            Scan;
  923.  
  924.         ERROR((HotPointX < 0) || (HotPointX >= 16) || (HotPointY < 0) || (HotPointY >= 16),
  925.             PRERR(AllowResume,"SetTheCursor:  hot point out of range"));
  926.         for (Scan = 0; Scan < 16; Scan += 1)
  927.             {
  928.                 TheCurs.data[Scan] = CursorImage[Scan];
  929.                 TheCurs.mask[Scan] = CursorMask[Scan];
  930.             }
  931.         TheCurs.hotSpot.h = HotPointX;
  932.         TheCurs.hotSpot.v = HotPointY;
  933.         SetCursor(&TheCurs);
  934.     }
  935.  
  936.  
  937. /* get the number of seconds to wait before toggling an insertion point */
  938. double                GetCursorBlinkRate(void)
  939.     {
  940.         return ((double)GetCaretTime()) / 60;
  941.     }
  942.  
  943.  
  944. /* get the maximum time between clicks for which they are considered a double click */
  945. double                GetDoubleClickInterval(void)
  946.     {
  947.         return ((double)GetDblTime()) / 60;
  948.     }
  949.  
  950.  
  951. /* emit a not too annoying beep to indicate an error occurred */
  952. void                    ErrorBeep(void)
  953.     {
  954.         if (MakeErrorBeeps)
  955.             {
  956.                 SysBeep(BEEPDURATION);
  957.             }
  958.          else
  959.             {
  960.                 unsigned long            StartTime;
  961.  
  962.                 FlashMenuBar(0);
  963.                 StartTime = TickCount();
  964.                 while (TickCount() - StartTime < MENUDELAY)
  965.                     {
  966.                         /* hideous delay loop to flash menu bar so user can see it. */
  967.                         RelinquishCPUCheckCancel();
  968.                     }
  969.                 FlashMenuBar(0);
  970.             }
  971.     }
  972.  
  973.  
  974. /* enable or disable error beeping.  True enables it.  the old value is returned. */
  975. MyBoolean                    SetErrorBeepEnable(MyBoolean ShouldWeBeep)
  976.     {
  977.         MyBoolean                OldValue;
  978.  
  979.         OldValue = MakeErrorBeeps;
  980.         MakeErrorBeeps = ShouldWeBeep;
  981.         return OldValue;
  982.     }
  983.  
  984.  
  985. /* this routine is called when a window is dying, so any locally cached pointers */
  986. /* to windows have to be discarded */
  987. void                    Eep_WindowDying(WinType* Window)
  988.     {
  989.         if ((WinType*)GetWRefCon(LastMouseDownInThisWindow) == Window)
  990.             {
  991.                 LastMouseDownInThisWindow = NIL;
  992.             }
  993.         if ((WinType*)GetWRefCon(LastActiveWindow) == Window)
  994.             {
  995.                 LastActiveWindow = NIL;
  996.             }
  997.     }
  998.